#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
技术信号生成器

职责：
1. 将模式识别结果转换为标准化交易信号
2. 计算具体执行动作（买入/卖出/观察）
3. 确定信号优先级和紧急程度
4. 生成人类可读的信号描述

信号格式：
{
    'pattern': 形态名称,
    'signal_type': 'buy' or 'sell',
    'action': 具体动作,
    'strength': 信号强度,
    'priority': 优先级,
    'urgency': 紧急程度,
    'reason': 原因描述,
    'timestamp': 时间戳,
    'details': {...}
}
"""

import logging
from typing import Dict, Optional, List
from datetime import datetime

logger = logging.getLogger(__name__)


class SignalPriority:
    """信号优先级定义（用于多信号决策）"""
    
    # 优先级定义（数值越大越优先）
    EMERGENCY_RISK = 10      # 紧急风控（如跌破止损）
    TECH_EMERGENCY = 9       # 技术紧急（评分≥3.5）
    RISK_CONTROL = 8         # 一般风控
    TECH_STRONG = 7          # 技术强卖出（评分≥3.0）
    STRATEGY_SELL = 6        # 策略卖出（如高位缩量）
    TECH_WEAK = 5            # 技术弱卖出（评分≥2.5）
    TECH_BUY_STRONG = 4      # 技术强买入
    TECH_BUY_WEAK = 3        # 技术弱买入
    STRATEGY_BUY = 2         # 策略买入
    OBSERVATION = 1          # 观察信号
    
    @classmethod
    def get_priority_name(cls, priority: int) -> str:
        """获取优先级名称"""
        priority_map = {
            10: '紧急风控',
            9: '技术紧急',
            8: '风险控制',
            7: '技术强卖',
            6: '策略卖出',
            5: '技术弱卖',
            4: '技术强买',
            3: '技术弱买',
            2: '策略买入',
            1: '观察'
        }
        return priority_map.get(priority, f'优先级{priority}')


class SignalGenerator:
    """技术信号生成器"""
    
    def __init__(self, config: Dict):
        """
        初始化信号生成器
        
        Args:
            config: 技术形态配置（完整的 technical_patterns.yaml）
        """
        self.config = config
        
        # 各形态的配置
        self.upper_shadow_config = config.get('upper_shadow', {})
        self.doji_config = config.get('doji', {})
        self.hammer_config = config.get('hammer', {})
        
        # 策略集成配置
        self.integration_config = config.get('strategy_integration', {})
        
        # 统计
        self.signal_count = {
            'buy': 0,
            'sell': 0,
            'neutral': 0
        }
    
    def generate_signal(self, 
                       pattern_result: Dict, 
                       position_info: Optional[Dict] = None) -> Optional[Dict]:
        """
        统一信号生成接口
        
        Args:
            pattern_result: 模式识别结果
            position_info: 持仓信息
        
        Returns:
            标准化的交易信号，如果不应生成信号则返回 None
        """
        if not pattern_result:
            return None
        
        pattern = pattern_result.get('pattern')
        signal_type = pattern_result.get('signal_type')
        
        # 根据形态类型分发
        if pattern == 'upper_shadow':
            if signal_type == 'sell':
                return self._generate_upper_shadow_sell(pattern_result, position_info)
            elif signal_type == 'buy':
                return self._generate_upper_shadow_buy(pattern_result, position_info)
        
        elif pattern == 'doji':
            return self._generate_doji_signal(pattern_result, position_info)
        
        elif pattern == 'hammer':
            return self._generate_hammer_signal(pattern_result, position_info)
        
        return None
    
    def _generate_upper_shadow_sell(self, 
                                   pattern_result: Dict, 
                                   position_info: Optional[Dict]) -> Dict:
        """
        生成上影线卖出信号
        
        根据信号强度决定：
        - action: 'sell_immediate' (立即清仓) or 'sell' (建议卖出) or 'warning' (警告观察)
        - urgency: 'emergency' or 'high' or 'normal'
        - priority: 9 / 7 / 5
        """
        strength = pattern_result['strength']
        details = pattern_result['details']
        stock_type = pattern_result['stock_type']
        
        # 获取阈值
        thresholds = self.upper_shadow_config.get('strength_threshold', {})
        emergency_threshold = thresholds.get('sell_emergency', 3.5)
        confirm_threshold = thresholds.get('sell_confirm', 3.0)
        suggest_threshold = thresholds.get('sell_suggest', 2.5)
        
        # 基础信号（保留config_key用于后续读取配置阈值）⭐
        signal = {
            'pattern': 'upper_shadow',
            'signal_type': 'sell',
            'strength': strength,
            'stock_type': stock_type,
            'config_key': pattern_result.get('config_key'),  # ⭐ 保留config_key
            'timestamp': datetime.now(),
            'details': details
        }
        
        # 根据强度决定动作和优先级
        if strength >= emergency_threshold:
            # 紧急卖出：立即清仓
            signal.update({
                'action': 'sell_immediate',
                'sell_ratio': 1.0,
                'urgency': 'emergency',
                'priority': SignalPriority.TECH_EMERGENCY,
                'reason': self._format_sell_reason(stock_type, strength, 'emergency', details)
            })
        
        elif strength >= confirm_threshold:
            # 确认卖出：建议清仓
            signal.update({
                'action': 'sell',
                'sell_ratio': 1.0,
                'urgency': 'high',
                'priority': SignalPriority.TECH_STRONG,
                'reason': self._format_sell_reason(stock_type, strength, 'high', details)
            })
        
        elif strength >= suggest_threshold:
            # 建议卖出：警告观察
            signal.update({
                'action': 'warning',
                'urgency': 'normal',
                'priority': SignalPriority.TECH_WEAK,
                'reason': self._format_sell_reason(stock_type, strength, 'normal', details)
            })
        
        else:
            # 强度不足，不生成信号
            return None
        
        # 添加持仓相关信息
        if position_info:
            signal['position_profit'] = position_info.get('profit_ratio', 0)
        
        # 统计
        self.signal_count['sell'] += 1
        
        return signal
    
    def _generate_upper_shadow_buy(self, 
                                  pattern_result: Dict, 
                                  position_info: Optional[Dict]) -> Optional[Dict]:
        """
        生成上影线买入信号（突破反包型）
        
        注意：当前版本可能仅记录日志，不实际触发
        """
        # 检查是否仅记录
        log_only = pattern_result.get('log_only', True)
        
        if log_only:
            # 仅记录，不生成实际信号（降低日志级别避免输出过多）
            logger.debug(f"检测到买入型上影线（仅记录）: {pattern_result['details']}")
            return None
        
        # 生成买入信号
        signal = {
            'pattern': 'upper_shadow',
            'signal_type': 'buy',
            'action': 'buy_enhance',
            'strength': pattern_result['strength'],
            'urgency': 'normal',
            'priority': SignalPriority.TECH_BUY_WEAK,
            'reason': f"突破反包上影线({pattern_result['stock_type']}股)",
            'timestamp': datetime.now(),
            'details': pattern_result['details']
        }
        
        self.signal_count['buy'] += 1
        
        return signal
    
    def _generate_doji_signal(self, 
                            pattern_result: Dict, 
                            position_info: Optional[Dict]) -> Optional[Dict]:
        """
        生成十字星信号
        
        十字星通常是观察信号，需要结合位置判断
        """
        signal_type = pattern_result['signal_type']
        strength = pattern_result['strength']
        details = pattern_result['details']
        
        signal = {
            'pattern': 'doji',
            'signal_type': signal_type,
            'strength': strength,
            'timestamp': datetime.now(),
            'details': details
        }
        
        if signal_type == 'sell':
            # 高位十字星 - 警告信号
            signal.update({
                'action': 'warning',
                'urgency': 'normal',
                'priority': SignalPriority.TECH_WEAK,
                'reason': f"高位十字星(位置{details['price_position']:.1%}) - 可能转折"
            })
            self.signal_count['sell'] += 1
        
        elif signal_type == 'buy':
            # 低位十字星 - 观察信号
            signal.update({
                'action': 'observation',
                'urgency': 'low',
                'priority': SignalPriority.OBSERVATION,
                'reason': f"低位十字星(位置{details['price_position']:.1%}) - 观察机会"
            })
            self.signal_count['buy'] += 1
        
        else:
            # 中位十字星 - 中性信号
            signal.update({
                'action': 'observation',
                'urgency': 'low',
                'priority': SignalPriority.OBSERVATION,
                'reason': f"十字星形态(位置{details['price_position']:.1%}) - 市场犹豫"
            })
            self.signal_count['neutral'] += 1
        
        return signal
    
    def _generate_hammer_signal(self, 
                              pattern_result: Dict, 
                              position_info: Optional[Dict]) -> Optional[Dict]:
        """
        生成锤子线信号
        
        锤子线通常是底部反转信号，需要等待确认
        """
        strength = pattern_result['strength']
        details = pattern_result['details']
        hammer_type = pattern_result.get('hammer_type', 'normal')
        
        # 获取阈值配置
        buy_threshold = self.hammer_config.get('buy_threshold', 2.0)
        
        if strength < buy_threshold:
            return None
        
        # 锤子类型描述
        type_name = {
            'normal': '锤子线',
            'inverted': '倒锤子线'
        }.get(hammer_type, '锤子线')
        
        signal = {
            'pattern': 'hammer',
            'signal_type': 'buy',
            'action': 'observation',  # 默认观察，等待确认
            'strength': strength,
            'urgency': 'normal',
            'priority': SignalPriority.TECH_BUY_WEAK if strength >= 2.5 else SignalPriority.OBSERVATION,
            'reason': f"{type_name}(评分{strength:.1f}分, 位置{details['price_position']:.1%}) - 底部反转迹象",
            'timestamp': datetime.now(),
            'details': details
        }
        
        self.signal_count['buy'] += 1
        
        return signal
    
    def _format_sell_reason(self, 
                           stock_type: str, 
                           strength: float,
                           urgency: str,
                           details: Dict) -> str:
        """格式化卖出原因"""
        type_name = {'large': '大盘股', 'medium': '中盘股', 'small': '小盘股'}.get(stock_type, stock_type)
        
        urgency_text = {
            'emergency': '【紧急】',
            'high': '【警告】',
            'normal': ''
        }.get(urgency, '')
        
        shadow_ratio = details.get('upper_shadow_ratio', 0)
        volume_ratio = details.get('volume_ratio', 0)
        price_position = details.get('price_position', 0)
        is_bearish = details.get('is_bearish', False)
        
        reason = f"{urgency_text}见顶上影线({type_name} 评分{strength:.1f}分)"
        
        # 添加关键特征
        features = []
        if shadow_ratio >= 0.7:
            features.append(f"上影线{shadow_ratio:.1%}")
        if volume_ratio >= 1.5:
            features.append(f"放量{volume_ratio:.2f}x")
        if price_position >= 0.85:
            features.append(f"高位{price_position:.1%}")
        if is_bearish:
            features.append("收阴")
        
        if features:
            reason += f" [{', '.join(features)}]"
        
        return reason
    
    def get_statistics(self) -> Dict:
        """获取信号统计"""
        return self.signal_count.copy()
    
    def reset_statistics(self):
        """重置统计"""
        self.signal_count = {
            'buy': 0,
            'sell': 0,
            'neutral': 0
        }


def merge_signals(strategy_signals: List[Dict], 
                 tech_signals: List[Dict],
                 risk_signals: List[Dict]) -> Optional[Dict]:
    """
    合并多来源信号，选择优先级最高的
    
    Args:
        strategy_signals: 策略信号
        tech_signals: 技术形态信号
        risk_signals: 风控信号
    
    Returns:
        最高优先级的信号，如果没有则返回None
    """
    all_signals = []
    
    # 添加策略信号
    for signal in strategy_signals:
        all_signals.append({
            **signal,
            'priority': signal.get('priority', SignalPriority.STRATEGY_SELL),
            'source': 'strategy'
        })
    
    # 添加技术信号
    for signal in tech_signals:
        all_signals.append({
            **signal,
            'source': 'technical'
        })
    
    # 添加风控信号
    for signal in risk_signals:
        all_signals.append({
            **signal,
            'source': 'risk'
        })
    
    if not all_signals:
        return None
    
    # 选择优先级最高的信号
    top_signal = max(all_signals, key=lambda x: x.get('priority', 0))
    
    return top_signal


def format_signal_summary(signal: Dict) -> str:
    """
    格式化信号摘要（用于日志输出）
    
    Args:
        signal: 信号字典
    
    Returns:
        格式化的信号摘要字符串
    """
    pattern_names = {
        'upper_shadow': '上影线',
        'doji': '十字星',
        'hammer': '锤子线'
    }
    
    pattern = pattern_names.get(signal.get('pattern'), signal.get('pattern', '未知'))
    signal_type = signal.get('signal_type', 'unknown')
    strength = signal.get('strength', 0)
    reason = signal.get('reason', '无原因')
    priority = signal.get('priority', 0)
    priority_name = SignalPriority.get_priority_name(priority)
    
    summary = (
        f"【{pattern}】{signal_type.upper()} 信号\n"
        f"  原因: {reason}\n"
        f"  强度: {strength:.2f}分\n"
        f"  优先级: {priority_name} ({priority})\n"
        f"  动作: {signal.get('action', 'N/A')}"
    )
    
    return summary

